package com.info.zhiduoduo.core.service.payment.thirdpay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.mapper.Condition;
import com.info.zhiduoduo.common.configs.CommonConfig;
import com.info.zhiduoduo.common.configs.KltPayConfig;
import com.info.zhiduoduo.common.configs.PayConfig;
import com.info.zhiduoduo.common.configs.PayTestModeConfig;
import com.info.zhiduoduo.common.configs.ZddUrlConfig;
import com.info.zhiduoduo.common.configs.ZhaoShangPayConfig;
import com.info.zhiduoduo.common.constants.Constants;
import com.info.zhiduoduo.common.constants.SmartResult;
import com.info.zhiduoduo.common.constants.StatusMsg;
import com.info.zhiduoduo.common.constants.company.CompanyAccountFlowBizType;
import com.info.zhiduoduo.common.constants.company.CompanyZhaoShangSubLedgerRechargeState;
import com.info.zhiduoduo.common.constants.company.CompanyZhaoshangSubLedgerIncomingRecordOperationType;
import com.info.zhiduoduo.common.constants.company.CompanyZhaoshangSubLedgerIncomingRecordState;
import com.info.zhiduoduo.common.constants.pay.ZddMerchantType;
import com.info.zhiduoduo.common.constants.pay.ZhaoShangContractState;
import com.info.zhiduoduo.common.exception.Asserts;
import com.info.zhiduoduo.common.exception.SmartException;
import com.info.zhiduoduo.common.utils.DateUtil;
import com.info.zhiduoduo.common.utils.LogUtil;
import com.info.zhiduoduo.common.utils.RandomUtil;
import com.info.zhiduoduo.common.utils.StringUtil;
import com.info.zhiduoduo.core.dto.zhaoshang.zhaoshangtradebulter.common.HeadRespDTO;
import com.info.zhiduoduo.core.dto.zhaoshang.zhaoshangtradebulter.common.ResponseDTO;
import com.info.zhiduoduo.core.dto.zhaoshang.zhaoshangtradebulter.request.*;
import com.info.zhiduoduo.core.dto.zhaoshang.zhaoshangtradebulter.response.*;
import com.info.zhiduoduo.core.dto.zhaoshang.zhaoshangtradebulter.response.detail.*;
import com.info.zhiduoduo.core.service.CommonService;
import com.info.zhiduoduo.core.service.CoreSensitiveService;
import com.info.zhiduoduo.core.service.WxMessageService;
import com.info.zhiduoduo.core.service.common.BaseService;
import com.info.zhiduoduo.repository.entity.company.*;
import com.info.zhiduoduo.repository.entity.pay.PaymentMerchant;
import com.info.zhiduoduo.repository.mapper.company.*;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class ZhaoShangTradeBulterService extends BaseService {

	@Autowired
	WxMessageService wxMessageService;
	@Resource
	CommonService commonService;
	@Autowired
	CoreSensitiveService coreSensitiveService;

	@Resource
	CompanyZhaoshangSubLedgerMapper companyZhaoshangSubLedgerMapper;
	@Resource
	CompanyZhaoshangSubLedgerRechargeMapper companyZhaoshangSubLedgerRechargeMapper;
	@Resource
	CompanyAccountMapper companyAccountMapper;
	@Resource
	CompanyZhaoshangSubLedgerIncomingRecordMapper companyZhaoshangSubLedgerIncomingRecordMapper;
	@Resource
	CompanyPaymentMapper companyPaymentMapper;

	@Autowired
	CommonConfig commonConfig;
	@Autowired
	ZddUrlConfig zddUrlConfig;
	@Autowired
	PayConfig payConfig;
	@Autowired
	KltPayConfig kltPayConfig;
	@Autowired
	PayTestModeConfig payTestModeConfig;
	@Autowired
	ZhaoShangPayConfig zhaoShangPayConfig;

	public CompanyZhaoshangSubLedger findCompanyZhaoShangSubLedger(int compid, int paymentMerchantId) {
		return companyZhaoshangSubLedgerMapper.findByCompidAndMerchantId(compid, paymentMerchantId);
	}

	@Transactional
	public CompanyZhaoshangSubLedger createSubLedgerIfNotExist(Company company, int merchantId) {
		CompanyZhaoshangSubLedger subLedger = findCompanyZhaoShangSubLedger(company.getCompid(), merchantId);
		if (subLedger != null) {
			return subLedger;
		}
		// 新建
		PaymentMerchant paymentMerchant = new PaymentMerchant().selectById(merchantId);
		if (paymentMerchant == null || paymentMerchant.getPaymentComp() != 3) {
			log.error("创建招行子账户出错，merchantId错误：" + merchantId);
			return null;
		}
		return createSubLedger(company.getCompid(), company.getCompname(), paymentMerchant);
	}

	@Transactional
	public CompanyZhaoshangSubLedger createSubLedger(int compid, String compname, PaymentMerchant paymentMerchant) {
		Asserts.isTrue(paymentMerchant.getPaymentComp() == 3, StatusMsg.StatusMsg_475);

		//调用第三方支付
		CompanyZhaoshangSubLedger subLedger = new CompanyZhaoshangSubLedger().setLedgerName(compname)
				.setCompid(compid)
				.setMerchantId(paymentMerchant.getId())
				.setAccountNo(paymentMerchant.getBankCard());
		boolean insert = subLedger.insert();
		Asserts.isTrue(insert, StatusMsg.StatusMsg_199);

		String ledgerNo = zhaoShangPayConfig.ledgerNo(subLedger.getId());
		if (payTestModeConfig.isTestMode()) {
			// Automatically create sub ledger in Test Mode without truely send the request
			subLedger.setLedgerNo(ledgerNo).updateById();
			return subLedger;
		}

		CreateSubLedgerFormBean formBean = new CreateSubLedgerFormBean();
		formBean.setLedgerNo(ledgerNo).setLedgerName(subLedger.getLedgerName()).setAccountNo(paymentMerchant.getBankCard());
		String url = zddUrlConfig.getBasePayUrl() + payConfig.getZhaoShangCreateSubLedgerUrl();
		LogUtil.info("创建招商记账子单元 url = " + url);

		SmartResult smartResult = new RestTemplate().postForObject(url, formBean, SmartResult.class);
		log.info("创建招商记账子单元，zdd-pay 返回结果：{}", JSON.toJSONString(smartResult));

		if (smartResult == null) {
			throw SmartException.of(StatusMsg.StatusMsg_473);
		} else if (smartResult.isFailed()) {
			// zdd-pay有报错, 直接返回返回报错
			throw SmartException.of(smartResult.getState(), smartResult.getMsg(), smartResult.getObject()).setList(smartResult.getList());
		}

		// 解析结果
		ResponseDTO<CreateSubLedgerRespDTO> responseDTO = JSON.parseObject(JSON.toJSONString(smartResult.getObject()),
				new TypeReference<ResponseDTO<CreateSubLedgerRespDTO>>() {
				});
		//获取head信息
		HeadRespDTO head = responseDTO.getHead();
		if (null == head) {
			log.error("创建招商记账子单元-失败！失败原因：head为空");
			throw SmartException.of(StatusMsg.StatusMsg_476);
		}
		if (!"SUC0000".equals(head.getResultcode())) {
			log.error("创建招商记账子单元-失败！失败原因：" + head.getResultmsg());
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}

		// 更新数据
		subLedger.setLedgerNo(ledgerNo).setReqid(head.getReqid()).setRspid(head.getRspid()).updateById();

		return subLedger;
	}

	/**
	 * 刷新所有新建中的记账子单元状态，看是否创建成功
	 */
	public void refreshCreateSubLedgerStatus() {
		companyZhaoshangSubLedgerMapper.listAllCreating().forEach(subLedger -> {
			try {
				refreshCreateSubLedgerStatus(subLedger);
			} catch (Exception e) {
				log.error("刷新新建中的记账子单元状态，报错", e);
			}
		});
	}

	/**
	 * 刷新记账子单元信息及状态
	 *
	 * @param subLedger
	 */
	public void refreshCreateSubLedgerStatus(CompanyZhaoshangSubLedger subLedger) {
		QuerySubLedgerTradeFormBean formBean = new QuerySubLedgerTradeFormBean().setAccountNo(subLedger.getAccountNo())
				.setLedgerNo(subLedger.getLedgerNo());
		String url = zddUrlConfig.getBasePayUrl() + payConfig.getZhaoShangQuerySubLedgerStatusUrl();
		LogUtil.info("刷新记账子单元信息及状态  url = " + url);

		SmartResult smartResult = new RestTemplate().postForObject(url, formBean, SmartResult.class);
		log.info("刷新记账子单元信息及状态，zdd-pay 返回结果：{}", JSON.toJSONString(smartResult));

		if (smartResult == null) {
			throw SmartException.of(StatusMsg.StatusMsg_473);
		} else if (smartResult.isFailed()) {
			// zdd-pay有报错, 直接返回返回报错
			throw SmartException.of(smartResult.getState(), smartResult.getMsg(), smartResult.getObject()).setList(smartResult.getList());
		}

		// 解析结果
		ResponseDTO<QuerySubLedgerStatusRespDTO> responseDTO = JSON.parseObject(JSON.toJSONString(smartResult.getObject()),
				new TypeReference<ResponseDTO<QuerySubLedgerStatusRespDTO>>() {
				});
		//获取head信息
		HeadRespDTO head = responseDTO.getHead();
		if (null == head) {
			log.error("刷新记账子单元信息及状态-失败！失败原因：head为空");
			throw SmartException.of(StatusMsg.StatusMsg_476);
		}
		if (!"SUC0000".equals(head.getResultcode())) {
			log.error("刷新记账子单元信息及状态-失败！失败原因：" + head.getResultmsg());
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}
		//获取body信息
		QuerySubLedgerStatusRespDTO body = responseDTO.getBody();
		//获取结果
		List<QuerySubLedgerStatusResult> ntdmalstz = body.getNtdmalstz();
		if (CollectionUtils.isEmpty(ntdmalstz) || CollectionUtils.isEmpty(ntdmalstz)) {
			log.error("刷新记账子单元信息及状态-失败！失败原因：返回responseDTO body结果为空");
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}

		QuerySubLedgerStatusResult statusResult = ntdmalstz.get(0);

		String stscod = statusResult.getStscod();
		if (StringUtil.equals(stscod, ZhaoShangContractState.VALID)) {
			// 更新数据
			subLedger.setState(2)
					.setCurrency(statusResult.getDmaccy())
					.setStscod(statusResult.getStscod())
					.setOverdraft(statusResult.getOvrctl())
					.setRltchk(statusResult.getRltchk())
					.setTlyopr(statusResult.getTlyopr())
					.setBcktyp(statusResult.getBcktyp())
					.setClstyp(statusResult.getClstyp())
					.setEffectiveDate(DateUtil.parseDate(statusResult.getEftdat(), DateUtil.FORMAT_STYLE_5))
					.updateById();
		}
	}

	/**
	 * 刷新所有创建成功的记账子单元的余额信息
	 */
	public void refreshSubLedgerBalance() {
		// retrieve all the balance of subledgers of each zhaoshang merchant account
		ZddMerchantType.getZhaoShangMerchantIdsExistJiuGe().stream()
				// Retrive the PaymentMerchant of each Zhaoshang merchant account
				.map(merchantId -> new PaymentMerchant().selectById(merchantId)).flatMap(paymentMerchant -> {
					try {
						// Retrieve all the balance of subledgers.
						return refreshSubLedgerBalance(paymentMerchant).stream();
					} catch (Exception e) {
						log.error("刷新招商银行商户号的所有子单元余额报错，商户号：{}", paymentMerchant.getName(), e);
					}
					return Stream.empty();
				}).map(balanceResult -> {
					// Update the balance
					CompanyZhaoshangSubLedger subLedger = companyZhaoshangSubLedgerMapper.findByLedgerNo(balanceResult.getDmanbr());
					if (subLedger == null) return null;

					subLedger.setBalance(new BigDecimal(balanceResult.getActbal())).updateById();
					return subLedger;
				})
				// Filter all the zero balances
				.filter(subLedger -> subLedger != null && subLedger.getBalance().doubleValue() > 0)
				// Filter all the frozen company
				.filter(subLeger -> {
					Company company = new Company().selectById(subLeger.getCompid());
					return company != null && company.getState() == Constants.comp_state_4;
				})
				// Refresh incoming records and transfer balance to principal account.
				.forEach(subLedger -> {
					log.info("开始刷新招商银行记账子单元入账流水和转账：ledgerNo = {}, companyName = {}", subLedger.getLedgerNo(), subLedger.getLedgerName());
					// 刷新招商银行的汇款流水记录
					refreshSubLedgerIncomingRecords(subLedger);

					// 开始转账
					transact(() -> transferBalance2Principal(subLedger));
				});
		/*companyZhaoshangSubLedgerMapper.listAllCreated().forEach(subLedger -> {
			try {
				// 刷新记账子单元的余额
				refreshSubLedgerBalance(subLedger);
				// 刷新招商银行的汇款流水记录
				refreshSubLedgerIncomingRecords(subLedger);

				// 开始转账
				if (subLedger.getBalance().doubleValue() > 0) {
					transact(() -> transferBalance2Principal(subLedger));
				}
			} catch (Exception e) {
				log.error("查询记账子单元的余额信息，报错", e);
			}
		});*/
	}

	/**
	 * 在中午12点以后，早上6点之前，充值记录需要延迟处理
	 *
	 * @return
	 */
	private boolean needDelayTransfer() {
		LocalTime start = LocalTime.of(6, 0);
		LocalTime end = LocalTime.of(12, 0);
		LocalTime now = LocalTime.now();
		return now.isAfter(end) || now.isBefore(start);
	}

	public List<QuerySubLedgerBalanceResult> refreshSubLedgerBalance(PaymentMerchant paymentMerchant) {
		QuerySubLedgerBalanceFormBean formBean = new QuerySubLedgerBalanceFormBean();
		// LedgerNo is empty means retrieve all the subledgers of this zhaoshang merchant account.
		formBean.setAccountNo(paymentMerchant.getBankCard()).setLedgerNo("");
		String url = zddUrlConfig.getBasePayUrl() + payConfig.getZhaoShangQuerySubLedgerBalanceUrl();
		LogUtil.info("查询记账子单元的余额信息  url = " + url);

		SmartResult smartResult = new RestTemplate().postForObject(url, formBean, SmartResult.class);
		log.info("查询记账子单元的余额信息，zdd-pay 返回结果：{}", JSON.toJSONString(smartResult));

		if (smartResult == null) {
			wxMessageService.tradeNetErrorNotifyAdmin();
			throw SmartException.of(StatusMsg.StatusMsg_473);
		} else if (smartResult.isFailed()) {
			// zdd-pay有报错, 直接返回返回报错
			throw SmartException.of(smartResult.getState(), smartResult.getMsg(), smartResult.getObject()).setList(smartResult.getList());
		}

		// 解析结果
		ResponseDTO<QuerySubLedgerBalanceRespDTO> responseDTO = JSON.parseObject(JSON.toJSONString(smartResult.getObject()),
				new TypeReference<ResponseDTO<QuerySubLedgerBalanceRespDTO>>() {
				});
		//获取head信息
		HeadRespDTO head = responseDTO.getHead();
		if (null == head) {
			log.error("查询记账子单元的余额信息-失败！失败原因：head为空");
			throw SmartException.of(StatusMsg.StatusMsg_476);
		}
		if (!"SUC0000".equals(head.getResultcode())) {
			log.error("查询记账子单元的余额信息-失败！失败原因：" + head.getResultmsg());
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}
		//获取body信息
		QuerySubLedgerBalanceRespDTO body = responseDTO.getBody();
		//获取结果
		List<QuerySubLedgerBalanceResult> ntdmabalz = body.getNtdmabalz();
		if (CollectionUtils.isEmpty(ntdmabalz)) {
			log.error("查询记账子单元的余额信息-失败！失败原因：返回responseDTO body结果为空");
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}
		return ntdmabalz;
	}

	public void refreshSubLedgerBalance(CompanyZhaoshangSubLedger subLedger) {
		QuerySubLedgerBalanceFormBean formBean = new QuerySubLedgerBalanceFormBean();
		formBean.setAccountNo(subLedger.getAccountNo()).setLedgerNo(subLedger.getLedgerNo());
		String url = zddUrlConfig.getBasePayUrl() + payConfig.getZhaoShangQuerySubLedgerBalanceUrl();
		LogUtil.info("查询记账子单元的余额信息  url = " + url);

		SmartResult smartResult = new RestTemplate().postForObject(url, formBean, SmartResult.class);
		log.info("查询记账子单元的余额信息，zdd-pay 返回结果：{}", JSON.toJSONString(smartResult));

		if (smartResult == null) {
			throw SmartException.of(StatusMsg.StatusMsg_473);
		} else if (smartResult.isFailed()) {
			// zdd-pay有报错, 直接返回返回报错
			throw SmartException.of(smartResult.getState(), smartResult.getMsg(), smartResult.getObject()).setList(smartResult.getList());
		}

		// 解析结果
		ResponseDTO<QuerySubLedgerBalanceRespDTO> responseDTO = JSON.parseObject(JSON.toJSONString(smartResult.getObject()),
				new TypeReference<ResponseDTO<QuerySubLedgerBalanceRespDTO>>() {
				});
		//获取head信息
		HeadRespDTO head = responseDTO.getHead();
		if (null == head) {
			log.error("查询记账子单元的余额信息-失败！失败原因：head为空");
			throw SmartException.of(StatusMsg.StatusMsg_476);
		}
		if (!"SUC0000".equals(head.getResultcode())) {
			log.error("查询记账子单元的余额信息-失败！失败原因：" + head.getResultmsg());
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}
		//获取body信息
		QuerySubLedgerBalanceRespDTO body = responseDTO.getBody();
		//获取结果
		List<QuerySubLedgerBalanceResult> ntdmabalz = body.getNtdmabalz();
		if (CollectionUtils.isEmpty(ntdmabalz)) {
			log.error("查询记账子单元的余额信息-失败！失败原因：返回responseDTO body结果为空");
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}
		QuerySubLedgerBalanceResult balanceResult = ntdmabalz.get(0);

		// 更新数据
		if (balanceResult != null) {
			subLedger.setBalance(new BigDecimal(balanceResult.getActbal())).updateById();
		}
	}

	public void transferBalance2Principal(CompanyZhaoshangSubLedger subLedger) {
		// 检查当前的子单元是否有正在转帐中的订单，如果有则需要等待它完成后才能继续操作。
		int count = companyZhaoshangSubLedgerRechargeMapper.existsProcessingOrder(subLedger.getId());
		if (count == 0) {
			// Retrieve all the pending incoming records. If there is no record, then just return, waiting for next round.
			List<CompanyZhaoshangSubLedgerIncomingRecord> list = companyZhaoshangSubLedgerIncomingRecordMapper.listAllPending(
					subLedger.getId());
			if (CollectionUtils.isEmpty(list)) return;

			// Check the sensitive words in the transaction note. If the note contains sensitive words, then it will be labeled as needing
			// manual operate.
			List<CompanyZhaoshangSubLedgerIncomingRecord> autoRechargeList = Lists.newArrayList();
			List<CompanyZhaoshangSubLedgerIncomingRecord> manualList = Lists.newArrayList();
			for (CompanyZhaoshangSubLedgerIncomingRecord entity : list) {
				// 判断是否企业是否有该商户配置
				CompanyPayment companyPayment = companyPaymentMapper.getCompanyMerchant(entity.getCompid(),entity.getMerchantId());
				if (companyPayment == null) {
					entity.setOperationType(CompanyZhaoshangSubLedgerIncomingRecordOperationType.PENDING)
						  .setState(CompanyZhaoshangSubLedgerIncomingRecordState.VERIFYING)
						  .setNote("请检查企业是否配置有对应主体支付方式，否则会导致充值失败");
					manualList.add(entity);
				} else if (StringUtil.isNotEmpty(coreSensitiveService.checkSensitiveWords(entity.getTrxtxt()))) {
					entity.setOperationType(CompanyZhaoshangSubLedgerIncomingRecordOperationType.PENDING)
							.setState(CompanyZhaoshangSubLedgerIncomingRecordState.VERIFYING);
					manualList.add(entity);
					// Send a Wechat message to the Cashier "纪锦娟"
					wxMessageService.notifyCashierCompanyZhaoShangSubLedgerIncomingRecordSensitive(entity);
				} else {
					// No sensitive words, it can be auto recharged.
					// 2022.6.13 延迟充值功能：s上午打款立刻到账，中午12点之后打款默认次日6点充值，财务也可以在后台点击‘立即充值‘。//2022.9.16 去掉延迟功能
//					if (needDelayTransfer()) {
//						entity.setState(CompanyZhaoshangSubLedgerIncomingRecordState.DELAYED);
//					}
					entity.setState(CompanyZhaoshangSubLedgerIncomingRecordState.PENDING)
						  .setOperationType(CompanyZhaoshangSubLedgerIncomingRecordOperationType.AUTO_RECHARGE);
					autoRechargeList.add(entity);
				}
				entity.updateById();
			}

			// Check the total amount of the records which can be auto recharged. We compute this amount by substracting the manual
			// amount from the balance.
			BigDecimal manualAmount = manualList.stream()
					.map(CompanyZhaoshangSubLedgerIncomingRecord::getTrxamt)
					.map(BigDecimal::new)
					.reduce(BigDecimal::add)
					.orElse(BigDecimal.ZERO);

			if (subLedger.getBalance().compareTo(manualAmount) <= 0) {
				// The balance of subLedger is insufficient for manual operation, leaving all of it to manual operation (manual recharge
				// or refunding).
				// We don't do the auto recharging. Change all the auto recharge records' state to HANDLED.
				for (CompanyZhaoshangSubLedgerIncomingRecord record : autoRechargeList) {
					record.setState(CompanyZhaoshangSubLedgerIncomingRecordState.HANDLED).updateById();
				}
				return;
			}

			// Empty list means no record should be transfered or they are in delayed state.
			// 但是这里可能会存在一个罕见问题。如果接口查出子单元的余额大于0，并且比需要手动处理的余额多，而且也在延迟处理的时间范围外，
			// 但autoRechargeList为空（即就是没有转账记录），那就转不了账了！
			if (CollectionUtils.isEmpty(autoRechargeList)) return;

			// 创建交易记录, This is the amount that should be auto recharged.
			BigDecimal authRechargeAmount = subLedger.getBalance().subtract(manualAmount);
			CompanyZhaoshangSubLedgerRecharge recharge = doZhaoshangSubLedgerRecharge(subLedger, authRechargeAmount);
			if (recharge == null) return;

			// Save the recharge id into incoming records, and set the state to PROCESSING
			for (CompanyZhaoshangSubLedgerIncomingRecord record : autoRechargeList) {
				record.setRechargeId(recharge.getId()).setState(CompanyZhaoshangSubLedgerIncomingRecordState.PROCESSING).updateById();
			}
		}
	}

	/**
	 * recharge the money to principal's account.
	 *
	 * @param subLedger company's ledger
	 * @param amount    The real amount that need to be recharge into the principal's account.
	 * @return
	 */
	private CompanyZhaoshangSubLedgerRecharge doZhaoshangSubLedgerRecharge(CompanyZhaoshangSubLedger subLedger, BigDecimal amount) {
		CompanyZhaoshangSubLedgerRecharge recharge = new CompanyZhaoshangSubLedgerRecharge().setLedgerId(subLedger.getId())
				.setAmount(amount)
				.setCompid(subLedger.getCompid())
				.setMerchantId(subLedger.getMerchantId())
				.setOrderNo(RandomUtil.createRandomNo("SLR", ""))
				.setAccountNo(subLedger.getAccountNo())
				.setPayerLedgerNo(subLedger.getLedgerNo())
				.setPayeeLedgerNo(zhaoShangPayConfig.ledgerNo(0))
				.setNote("企业充值")
				.setState(CompanyZhaoShangSubLedgerRechargeState.CREATED);
		if (!recharge.insert()) return null;

		SubLedgerPayFormBean formBean = new SubLedgerPayFormBean();
		formBean.setAmount(amount.setScale(2, RoundingMode.DOWN).toString())
				.setAccountNo(subLedger.getAccountNo())
				.setPayerLedgerNo(subLedger.getLedgerNo())
				.setPayeeLedgerNo(zhaoShangPayConfig.ledgerNo(0))
				.setNote("企业充值");

		String url = zddUrlConfig.getBasePayUrl() + payConfig.getZhaoShangSubLedgerPayUrl();
		LogUtil.info("记账子单元充值转账给主体账户  url = " + url);

		SmartResult smartResult = new RestTemplate().postForObject(url, formBean, SmartResult.class);
		log.info("记账子单元充值转账给主体账户，zdd-pay 返回结果：{}", JSON.toJSONString(smartResult));

		if (smartResult == null) {
			throw SmartException.of(StatusMsg.StatusMsg_473);
		} else if (smartResult.isFailed()) {
			// zdd-pay有报错, 直接返回返回报错
			throw SmartException.of(smartResult.getState(), smartResult.getMsg(), smartResult.getObject()).setList(smartResult.getList());
		}

		// 解析结果
		ResponseDTO<SubLedgerPayRespDTO> responseDTO = JSON.parseObject(JSON.toJSONString(smartResult.getObject()),
				new TypeReference<ResponseDTO<SubLedgerPayRespDTO>>() {
				});
		//获取head信息
		HeadRespDTO head = responseDTO.getHead();
		if (null == head) {
			log.error("记账子单元充值转账给主体账户-失败！失败原因：head为空");
			throw SmartException.of(StatusMsg.StatusMsg_476);
		}
		if (!"SUC0000".equals(head.getResultcode())) {
			log.error("记账子单元充值转账给主体账户-失败！失败原因：" + head.getResultmsg());
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}
		recharge.setReqid(head.getReqid()).setRspid(head.getRspid());

		//获取body信息
		SubLedgerPayRespDTO body = responseDTO.getBody();

		List<SubLedgerPayResult> ntdmatrxz1 = body.getNtdmatrxz1();
		if (CollectionUtils.isEmpty(ntdmatrxz1)) {
			log.error("记账子单元充值转账给主体账户-失败！失败原因：返回responseDTO body结果为空");
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}
		for (SubLedgerPayResult result : ntdmatrxz1) {
			if (StringUtil.equals(result.getTrxdir(), "D")) {
				// 付款方
				recharge.setPayerTrxnbr(result.getTrxnbr())
						.setTransactionDate(DateUtil.parseDate(result.getTrxdat(), DateUtil.FORMAT_STYLE_5));
			} else if (StringUtil.equals(result.getTrxdir(), "C")) {
				// 收款方
				recharge.setPayeeTrxnbr(result.getTrxnbr())
						.setTransactionDate(DateUtil.parseDate(result.getTrxdat(), DateUtil.FORMAT_STYLE_5));
			}
		}

		recharge.setState(CompanyZhaoShangSubLedgerRechargeState.PAYING).updateById();
		return recharge;
	}

	public void transferLedgerIncoming2Principal(CompanyZhaoshangSubLedgerIncomingRecord record) {
		Integer ledgerId = record.getLedgerId();
		CompanyZhaoshangSubLedger ledger = new CompanyZhaoshangSubLedger().selectById(ledgerId);
		Asserts.notNull(ledger, StatusMsg.StatusMsg_479);

		// 检查当前的子单元是否有正在转帐中的订单，如果有则需要等待它完成后才能继续操作。
		int count = companyZhaoshangSubLedgerRechargeMapper.existsProcessingOrder(ledgerId);
		Asserts.isTrue(count == 0, StatusMsg.StatusMsg_480);

		// Must guarantee that the ledger balance is sufficient.
		BigDecimal balance = ledger.getBalance();
		BigDecimal amount = new BigDecimal(record.getTrxamt());
		Asserts.isTrue(balance.doubleValue() > 0, StatusMsg.StatusMsg_481);
		// If the ledger's balance is less than amount, then we transfer all the balance to default account. Otherwise, we transfer the
		// record's amount to default account.
		amount = balance.compareTo(amount) <= 0 ? balance : amount;

		CompanyZhaoshangSubLedgerRecharge recharge = doZhaoshangSubLedgerRecharge(ledger, amount);
		Asserts.notNull(recharge, StatusMsg.StatusMsg_477);

		// Change the state of record
		record.setRechargeId(recharge.getId()).setState(CompanyZhaoshangSubLedgerIncomingRecordState.PROCESSING).updateById();
	}

	public void refreshSubLedgerPay() {
		companyZhaoshangSubLedgerRechargeMapper.listAllProcessing().forEach(order -> {
			try {
				refreshSubLedgerPay(order);
			} catch (Exception e) {
				log.error("查询记账子单元的转账充值结果，报错", e);
			}
		});
	}

	public void refreshSubLedgerPay(CompanyZhaoshangSubLedgerRecharge recharge) {
		QuerySubLedgerTradeFormBean formBean = new QuerySubLedgerTradeFormBean().setAccountNo(recharge.getAccountNo())
																				.setLedgerNo(recharge.getPayerLedgerNo());
		String url = zddUrlConfig.getBasePayUrl() + payConfig.getZhaoShangQuerySubLedgerTodayTradeUrl();
		LogUtil.info("查询记账子单元的转账充值  url = " + url);

		SmartResult smartResult = new RestTemplate().postForObject(url, formBean, SmartResult.class);
		log.info("查询记账子单元的转账充值，zdd-pay 返回结果：{}", JSON.toJSONString(smartResult));

		if (smartResult == null) {
			throw SmartException.of(StatusMsg.StatusMsg_473);
		} else if (smartResult.isFailed()) {
			// zdd-pay有报错, 直接返回返回报错
			throw SmartException.of(smartResult.getState(), smartResult.getMsg(), smartResult.getObject()).setList(smartResult.getList());
		}

		// 解析结果
		ResponseDTO<QuerySubLedgerTradeRespDTO> responseDTO = JSON.parseObject(JSON.toJSONString(smartResult.getObject()),
				new TypeReference<ResponseDTO<QuerySubLedgerTradeRespDTO>>() {
				});
		//获取head信息
		HeadRespDTO head = responseDTO.getHead();
		if (null == head) {
			log.error("查询记账子单元的转账充值-失败！失败原因：head为空");
			throw SmartException.of(StatusMsg.StatusMsg_476);
		}
		if (!"SUC0000".equals(head.getResultcode())) {
			log.error("查询记账子单元的转账充值-失败！失败原因：" + head.getResultmsg());
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}
		//获取body信息
		QuerySubLedgerTradeRespDTO body = responseDTO.getBody();
		//获取结果
		List<QuerySubLedgerTradeResult> ntdmtlstz = body.getNtdmtlstz();
		if (CollectionUtils.isEmpty(ntdmtlstz)) {
			log.error("查询记账子单元的转账充值-失败！失败原因：返回responseDTO body结果为空");
			throw SmartException.of(StatusMsg.StatusMsg_477);
		}

		for (QuerySubLedgerTradeResult result : ntdmtlstz) {
			// 用支付方也就是记账子单元的流水号去匹配
			if (StringUtil.equals(result.getTrxnbr(), recharge.getPayerTrxnbr())) {
				recharge.setTransactionTime(DateUtil.parseDate(result.getTrxtim(), "hhMMss"))
						.setState(CompanyZhaoShangSubLedgerRechargeState.SUCCESS)
						.setFinishtime(new Date())
						.updateById();
				// Change the state of incoming records
				companyZhaoshangSubLedgerIncomingRecordMapper.rechargeSuccessed(recharge.getId());
				zhaoShangSubLedgerRecharged(recharge);
				break;
			}
		}
	}

	/**
	 * 确认充值完成,同步锁,防止2个请求同时操作一个充值记录
	 */
	public void zhaoShangSubLedgerRecharged(CompanyZhaoshangSubLedgerRecharge recharge) {
		if (recharge == null || recharge.getState() != CompanyZhaoShangSubLedgerRechargeState.SUCCESS) {
			return;
		}

		//企业账户是否存在
		Integer compid = recharge.getCompid();
		Integer merchantId = recharge.getMerchantId();
		CompanyPayment companyPayment = companyPaymentMapper.getCompanyMerchant(compid,merchantId);
		if (companyPayment == null){
			log.error("记账子单元的转账充值到企业余额-失败！失败原因：查询企业余额配置为空，compid：{},merchantId：{}", compid, merchantId);
			return;
		}
		Company company = new Company().selectById(compid);
		CompanyAccount account = new CompanyAccount().selectOne(Condition.create().eq("compid", compid));
		if (account == null){
			log.error("记账子单元的转账充值到企业余额-失败！失败原因：查询企业账户为空，compid：{},merchantId：{}", compid, merchantId);
			return;
		}

		//账户余额增加
		BigDecimal amount = recharge.getAmount();
		BigDecimal beforeBalance = companyPayment.getBalance();
		BigDecimal frozenBalance = companyPayment.getFrozenBalance();
		BigDecimal currBalance = amount.add(beforeBalance);

		companyPayment.setBalance(currBalance);

		if (companyPayment.updateById()) {
			//确认完成,生成企业流水
			String flowname = Constants.ACCT_FLOW_RECHARGE;
			boolean insertFLow = new CompanyAccountflow().setCompid(compid)
					.setFlowAmount(amount)
					.setBeforeBalance(beforeBalance)
					.setCurrBalance(currBalance)
					.setBeforeFrozenBalance(frozenBalance)
					.setCurrFrozenBalance(frozenBalance)
					.setBizid(recharge.getId())
					.setBizType(CompanyAccountFlowBizType.SUB_LEDGER_RECHARGE)
					.setFlowtype(1)
					.setFlowname(flowname)
					.setPayId(companyPayment.getPayId())
					.insert();

			if (insertFLow) {
				log.info("查询记账子单元的转账充值, 发送微信通知");
				new Thread(() -> {
					String info = String.format("【%s】，充值金额：【%s】元", company.getCompname(), recharge.getAmount());
					// 给顾问发企信消息
					commonService.sendEnterpriseWeChatNotice(account.getBusinessManagerId(), company.getCompname(), 1, info);
					//成功,通知企业
					wxMessageService.notifyCompanyZhaoShangSubLedgerRecharged(recharge);
					// 通知财务符嘉琳
					wxMessageService.notifyCashierCompanyZhaoShangSubLedgerRecharged(recharge);
				}).start();
			} else {
				try {
					throw new RuntimeException("生成企业流水失败");
				} catch (RuntimeException e) {
					e.printStackTrace();
					//手动回滚
					TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
				}
			}
		} else {
			try {
				throw new RuntimeException("企业账户余额更新失败");
			} catch (RuntimeException e) {
				e.printStackTrace();
				//手动回滚
				TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			}
		}
	}

/*	public void refreshSubLedgerIncomingRecords() {
		// 获取所有的记账子单元，遍历更新账户汇入记录
		companyZhaoshangSubLedgerMapper.listAllCreated().parallelStream().forEach(this::refreshSubLedgerIncomingRecords);
	}*/

	public void refreshSubLedgerIncomingRecords(CompanyZhaoshangSubLedger ledger) {
		// Retrieve all the transaction records from the API
		List<QuerySubLedgerTradeResult> list = retrieveTransactionRecordsFromApi(ledger);
		if (CollectionUtils.isEmpty(list)) return;

		// Filter all the transaction records, we just want to persist the incoming record.
		list = list.stream().filter(result -> StringUtil.equals(result.getTrxdir(), "C")).collect(Collectors.toList());

		// Retrieve all the incoming records from our database
		List<CompanyZhaoshangSubLedgerIncomingRecord> savedList =
				companyZhaoshangSubLedgerIncomingRecordMapper.listAllByCompidLedgerIdDate(
				ledger.getCompid(), ledger.getId(), DateUtil.nowDate());

		// Then compare these two list to find out the new records that haven't saved in our database.
		// The key is the transaction No., the value list should always have only one element since the transaction No. is unique.
		Map<String, List<CompanyZhaoshangSubLedgerIncomingRecord>> savedMap = savedList.stream()
				.collect(Collectors.groupingBy(CompanyZhaoshangSubLedgerIncomingRecord::getTrxnbr));
		for (QuerySubLedgerTradeResult result : list) {
			// Already exists, just ignore it.
			if (savedMap.containsKey(result.getTrxnbr())) continue;

			// This is a new record, insert it into database
			CompanyZhaoshangSubLedgerIncomingRecord entity = result.toEntity(DateUtil.nowDate(), ledger);
			entity.insert();
		}
	}

	private List<QuerySubLedgerTradeResult> retrieveTransactionRecordsFromApi(CompanyZhaoshangSubLedger ledger) {
		// Save all the records into this list
		List<QuerySubLedgerTradeResult> list = Lists.newArrayList();

		// There will be a subsequent list depending on whether the field "rsv50z" in the response object is empty or not.
		String rsv50z = "";
		do {
			QuerySubLedgerTradeFormBean formBean = new QuerySubLedgerTradeFormBean().setAccountNo(ledger.getAccountNo())
					.setLedgerNo(ledger.getLedgerNo())
					.setRsv50z(rsv50z);
			String url = zddUrlConfig.getBasePayUrl() + payConfig.getZhaoShangQuerySubLedgerTodayTradeUrl();
			LogUtil.info("查询记账子单元的转账汇入记录  url = " + url);

			SmartResult smartResult = new RestTemplate().postForObject(url, formBean, SmartResult.class);
			log.info("查询记账子单元的转账汇入记录，zdd-pay 返回结果：{}", JSON.toJSONString(smartResult));

			if (smartResult == null) {
				break;
			} else if (smartResult.isFailed()) {
				// zdd-pay有报错, 直接返回返回报错
				break;
			}

			// 解析结果
			ResponseDTO<QuerySubLedgerTradeRespDTO> responseDTO = JSON.parseObject(JSON.toJSONString(smartResult.getObject()),
					new TypeReference<ResponseDTO<QuerySubLedgerTradeRespDTO>>() {
					});
			//获取head信息
			HeadRespDTO head = responseDTO.getHead();
			if (null == head) {
				log.error("查询记账子单元的转账汇入记录-失败！失败原因：head为空");
				break;
			}
			if (!"SUC0000".equals(head.getResultcode())) {
				log.error("查询记账子单元的转账汇入记录-失败！失败原因：" + head.getResultmsg());
				break;
			}
			//获取body信息
			QuerySubLedgerTradeRespDTO body = responseDTO.getBody();
			//获取结果
			List<QuerySubLedgerTradeResult> ntdmtlstz = body.getNtdmtlstz();
			if (CollectionUtils.isNotEmpty(ntdmtlstz)) {
				list.addAll(ntdmtlstz);
			}

			// Subsequent keyword
			List<QuerySubLedgerTradeInfo> ntdmtlsty = body.getNtdmtlsty();
			if (CollectionUtils.isNotEmpty(ntdmtlsty)) {
				// Renew the subsequent keyword.
				rsv50z = ntdmtlsty.get(0).getRsv50z();
			} else {
				// Means there are no more subsequent records, set the variable "rsv50z" to an empty string.
				rsv50z = "";
			}
		}
		// Keep retrieving more records from the API if "rsv50z" is not empty.
		while (StringUtil.isNotEmpty(rsv50z));

		// Return the list which contains all the records.
		return list;
	}
}
